package com.mingqijia.gassafety.authority.vcc.filter;

import cn.hutool.json.JSONObject;
import com.gap.basic.base.CommonResponse;
import com.mingqijia.gassafety.authority.dal.helper.ConfigureHolder;
import com.mingqijia.gassafety.authority.domain.adapter.BenAdapter;
import com.mingqijia.gassafety.authority.vcc.user.VccUserLoginHolder;
import com.mingqijia.gassafety.authority.vcc.valueobject.VccUserVO;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.jwt.JwtProvider;
import com.mingqijia.gassafety.shared.jwt.JwtToken;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @author Damon S.
 * @version v1.0.1
 * @date 2020年07月30日 10:04
 */
@Slf4j
@Component
public class VccLoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final VccUserLoginHolder holder;
    private final BenAdapter benAdapter;
    private final JwtProvider jwtProvider;
    private final ConfigureHolder cHolder;


    public VccLoginAuthenticationFilter(JwtProvider jwtProvider,
                                        VccUserLoginHolder holder,
                                        BenAdapter benAdapter,
                                        ConfigureHolder cHolder) {
        this.jwtProvider = jwtProvider;
        this.holder = holder;
        this.benAdapter = benAdapter;
        this.cHolder = cHolder;
        setFilterProcessesUrl(Constants.TOKEN_URL);
    }

    @Autowired
    @Override
    public void setAuthenticationManager(AuthenticationManager manager) {
        super.setAuthenticationManager(manager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token;
        String type = obtainType(request);

        if (Constants.STR_TYPE_CREDENTIAL.equals(type)) {
            String appId = obtainAppId(request);
            String secret = obtainSecret(request);
            if (Objects.isNull(appId) || Objects.isNull(secret)) {
                throw new GlobalAuthenticationException("参数不合法");
            }
            // 临时处理
            if (!"alarm-gas".equals(appId) || !"2DFF80AE1F4E9BC1B4563C26672D80C2".equals(secret)) {
                throw new GlobalAuthenticationException("认证信息不合法");
            }
            token = new UsernamePasswordAuthenticationToken(
                    Constants.STR_SECRET + Constants.STR_SHARP + appId + Constants.STR_SHARP + secret, secret);
        } else if (Constants.STR_TYPE_TOKEN.equals(type)) {
            String tokenStr = obtainToken(request);
            token = new UsernamePasswordAuthenticationToken(
                    Constants.STR_TOKEN + Constants.STR_SHARP + tokenStr, tokenStr);
        } else {
            // 默认使用code模式
            String state = request.getParameter("state");
            String code = obtainCode(request);
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(Constants.STR_CODE)
                    .append(Constants.STR_SHARP).append(code)
                    .append(Constants.STR_SHARP).append(state);
            log.info("拼接的字符串:{}",stringBuilder.toString());
            token = new UsernamePasswordAuthenticationToken(
                    stringBuilder.toString(), code);
        }
        setDetails(request, token);

        return getAuthenticationManager().authenticate(token);
    }

    /**
     * 成功验证后调用的方法
     * 如果验证成功，就生成token并返回
     **/
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
        VccUserVO backend = (VccUserVO) auth.getPrincipal();

        // 认证码CODE方式登录的
        if (Constants.STR_CODE.equals(backend.getLoginType())) {
            // 燃气云方式登录
            JSONObject userDetail = holder.buildVccUserDetail(backend);
            // 更新登录时间。登录后将登出时间更新为毫秒，用于判断token过期
            holder.whenManagerLogin(backend);
            log.info("登录用户信息:[{}]", backend);

            benAdapter.checkBenDepart(userDetail.getStr("spName"), backend.getSpId());
            benAdapter.checkBenUser(backend.getUsername(), backend.getSpId());
            // 适配本安系统的用户登录
            benAdapter.login(backend.getUsername());

            ApplicationUtils.writeOutJson(response, CommonResponse.success(backend.getToken()));
            return;
        }

        // 非CODE模式登录（第三方token和客户认证模式）
        // 生成token并返回调用
        JwtToken.JwtTokenBuilder builder = JwtToken.builder()
                .uniqueName(backend.getUsername());
        // token 失效时间配置
        String expiredInStr = cHolder.getConfigByKey(
                Constants.CACHE_KEY_TOKEN_EXPIRY
        );
        if (StringUtils.hasText(expiredInStr)) {
            builder.expiredIn(Integer.parseInt(expiredInStr));
        }
        backend.setExpireTime(2 * 60 *60);
        backend.setToken(jwtProvider.generateToken(builder.build()));
        // 更新登录时间。登录后将登出时间更新为毫秒，用于判断token过期
        holder.whenManagerLogin(backend);

        ApplicationUtils.writeOutJson(response, CommonResponse.success(
                Constants.STR_SECRET.equals(backend.getLoginType()) ? backend.getToken() : backend
        ));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        String message = failed.getMessage();
        if (failed instanceof GlobalAuthenticationException) {
            if (((GlobalAuthenticationException) failed).isLogStack()) {
                log.error("安全认证异常", failed);
            }
        } else if (failed instanceof LockedException) {
            message = "账户已锁定/未激活";
        } else if (failed instanceof BadCredentialsException) {
            message = "用户名或密码错误";
        }
        ApplicationUtils.writeOutJson(response, CommonResponse.failure(message));
    }

    private String obtainCode(HttpServletRequest request) {
        return request.getParameter(Constants.STR_CODE);
    }

    private String obtainToken(HttpServletRequest request) {
        return request.getParameter(Constants.STR_TOKEN);
    }

    private String obtainAppId(HttpServletRequest request) {
        return request.getParameter(Constants.STR_APPID);
    }

    private String obtainSecret(HttpServletRequest request) {
        return request.getParameter(Constants.STR_SECRET);
    }

    private String obtainType(HttpServletRequest request) {
        return request.getParameter(Constants.STR_TYPE);
    }
}
